/// Observable[T,E] represents an effect that may return 0/finite/infinite
/// T values, or throw an E error half-way, synchronously or asynchronously.
type Observable [out T = unit, out E = never]
    native; // rx.Observable


export function with-canceller:[O < Observable[any,any]]
    &(&(Sync) => O) => O
    native '__WithCanceller';

export function take-one-as-single:[T,E]
    &(Observable[T,E]) => Async[Maybe[T],E]
    native '__ObservableTakeOneAsSingle';

export function reduce:[T,A,E]
    &(Observable[T,E], (A, &(A,T) => A)) => Async[A,E]
    native '__ObservableReduce';

export function wait-complete:[E]
    &(Observable[unit,E]) => Async[unit,E]
    native '__ObservableWaitComplete';

export function forever:[E]
    &(Observable[unit,E]) => Async[never,E]
    native '__ObservableForever';


export function start-with:[T,E]
    &(Observable[T,E], List[T]) => Observable[T,E]
    native '__ObservableStartWith';

export function map:[A,B,E]
    &(Observable[A,E], &(A) => B) => Observable[B,E]
    native '__ObservableMap';

export function map-to:[A,B,E]
    &(Observable[A,E], B) => Observable[B,E]
    &(e,v) => e.{map(&(_) => v)};

export function filter:[T,E]
    &(Observable[T,E], &(T) => Bool) => Observable[T,E]
    native '__ObservableFilter';

export function deflate-map:[A,B,E]
    &(Observable[A,E], &(A) => Maybe[B]) => Observable[B,E]
    native '__ObservableDeflateMap';

export function scan:[T,A,E]
    &(Observable[T,E], (A, &(A,T) => A)) => Observable[A,E]
    native '__ObservableScan';

export function debounce:[T,E]
    &(Observable[T,E], Number) => Observable[T,E]
    native '__ObservableDebounce';

export function merge:[T,E]
    &(List[Observable[T,E]]) => Observable[T,E]
    native '__ObservableMerge';

export function concat:[T,E]
    &(List[Observable[T,E]]) => Observable[T,E]
    native '__ObservableConcat';

export function merge-map:[A,B,E]
    &(Observable[A,E], &(A) => Observable[B,E]) => Observable[B,E]
    native '__ObservableMergeMap';

export function concat-map:[A,B,E]
    &(Observable[A,E], &(A) => Observable[B,E]) => Observable[B,E]
    native '__ObservableConcatMap';

export function mix-map:[A,B,E]
    &(Observable[A,E], Number, &(A) => Observable[B,E]) => Observable[B,E]
    native '__ObservableMixMap';

export function switch-map:[A,B,E]
    &(Observable[A,E], &(A) => Observable[B,E]) => Observable[B,E]
    native '__ObservableSwitchMap';

export function exhaust-map:[A,B,E]
    &(Observable[A,E], &(A) => Observable[B,E]) => Observable[B,E]
    native '__ObservableExhaustMap';

function distinct-until-changed:[T,E]
    &(Observable[T,E], &(T,T) => Bool) => Observable[T,E]
    native '__ObservableDistinctUntilChanged';

export function distinct-until-changed:
    [T,E] { = : Op=[T] }
    &(Observable[T,E]) => Observable[T,E]
    &(a) => { distinct-until-changed (a, =) };

export function with-latest-from:[A,B,E]
    &(Observable[A,E], Observable[B,E]) => Observable[(A,Maybe[B]),E]
    native '__ObservableWithLatestFrom';

export function combine-latest:[A,B,E]
    &(Observable[A,E],Observable[B,E]) => Observable[(A,B),E]
    native '__ObservableCombineLatest';

export function combine-latest:[A,B,C,E]
    &(Observable[A,E],Observable[B,E],Observable[C,E]) => Observable[(A,B,C),E]
    native '__ObservableCombineLatest';

export function combine-latest:[A,B,C,D,E]
    &(Observable[A,E],Observable[B,E],Observable[C,E],Observable[D,E]) => Observable[(A,B,C,D),E]
    native '__ObservableCombineLatest';

export function combine-latest:[T,E]
    &(List[Observable[T,E]]) => Observable[List[T],E]
    native '__ObservableCombineLatestList';


